module phys_control
  !-----------------------------------------------------------------------
  ! Purpose:
  !
  ! Provides a control interface to CAM physics packages
  !
  ! Revision history:
  ! 2006-05-01  D. B. Coleman,  Creation of module
  ! 2009-02-13  Eaton           Replace *_{default,set}opts methods with module namelist.
  !                             Add vars to indicate physics version and chemistry type.
  !-----------------------------------------------------------------------

  use spmd_utils,     only: masterproc
  use cam_logfile,    only: iulog
  use cam_abortutils, only: endrun
  use shr_kind_mod,   only: r8 => shr_kind_r8
  
  implicit none
  private
  save
  
  public phys_ctl_readnl
  public phys_getopts
  public phys_setopts
  public phys_deepconv_pbl
  public phys_do_flux_avg
  public cam_physpkg_is
  public cam_chempkg_is
  public waccmx_is
  
  character(16), parameter :: unset_str = 'UNSET'
  integer,       parameter :: unset_int = huge(1)
  
  ! Namelist variables:
  character(16) :: cam_physpkg          = unset_str  ! CAM physics package
  character(32) :: cam_chempkg          = unset_str  ! CAM chemistry package
  character(16) :: waccmx_opt           = unset_str  ! WACCMX run option [ionosphere | neutral | off
  character(16) :: deep_scheme          = unset_str  ! deep convection package
  character(16) :: shallow_scheme       = unset_str  ! shallow convection package
  character(16) :: eddy_scheme          = unset_str  ! vertical diffusion package
  character(16) :: microp_scheme        = unset_str  ! microphysics package
  character(16) :: macrop_scheme        = unset_str  ! macrophysics package
  character(16) :: radiation_scheme     = unset_str  ! radiation package
  integer       :: srf_flux_avg         = unset_int  ! 1 => smooth surface fluxes, 0 otherwise
  
  logical       :: use_subcol_microp    = .false.    ! if .true. then use sub-columns in microphysics
  
  logical       :: atm_dep_flux         = .true.     ! true => deposition fluxes will be provided
                                                     ! to the coupler
  logical       :: history_amwg         = .true.     ! output the variables used by the AMWG diag package
  logical       :: history_vdiag        = .false.    ! output the variables used by the AMWG variability diag package
  logical       :: history_aerosol      = .false.    ! output the MAM aerosol variables and tendencies
  logical       :: history_aero_optics  = .false.    ! output the aerosol
  logical       :: history_eddy         = .false.    ! output the eddy variables
  logical       :: history_budget       = .false.    ! output tendencies and state variables for CAM4
                                                     ! temperature, water vapor, cloud ice and cloud
                                                     ! liquid budgets.
  logical       :: convproc_do_aer      = .false.    ! switch for new convective scavenging treatment for modal aerosols
  
  integer       :: history_budget_histfile_num = 1   ! output history file number for budget fields
  logical       :: history_waccm        = .false.    ! output variables of interest for WACCM runs
  logical       :: history_waccmx       = .false.    ! output variables of interest for WACCM-X runs
  logical       :: history_chemistry    = .true.     ! output default chemistry-related variables
  logical       :: history_carma        = .false.    ! output default CARMA-related variables
  logical       :: history_clubb        = .true.     ! output default CLUBB-related variables
  logical       :: history_cesm_forcing = .false.
  logical       :: history_dust         = .false.
  logical       :: history_scwaccm_forcing = .false.
  logical       :: history_chemspecies_srf = .false.
  
  logical       :: do_clubb_sgs
  ! Check validity of physics_state objects in physics_update.
  logical       :: state_debug_checks   = .false.
  
  ! Macro/micro-physics co-substeps
  integer       :: cld_macmic_num_steps = 1
  
  logical       :: offline_driver       = .false.    ! true => offline driver is being used
  
  
  logical, public, protected :: use_simple_phys = .false. ! true => simple physics configuration
  
  logical :: use_spcam       ! true => use super parameterized CAM
  
  logical :: prog_modal_aero ! determines whether prognostic modal aerosols are present in the run.
  
  ! Option to use heterogeneous freezing
  logical, public, protected :: use_hetfrz_classnuc = .false.
  
  ! Which gravity wave sources are used?
  logical, public, protected :: use_gw_oro        = .true.  ! Orography.
  logical, public, protected :: use_gw_front      = .false. ! Frontogenesis.
  logical, public, protected :: use_gw_front_igw  = .false. ! Frontogenesis to inertial spectrum.
  logical, public, protected :: use_gw_convect_dp = .false. ! Deep convection.
  logical, public, protected :: use_gw_convect_sh = .false. ! Shallow convection.
  
  ! FV dycore angular momentum correction
  logical, public, protected :: fv_am_correction  = .false.
  
  ! CAM snapshot before/after file numbers and control
  character(32) :: cam_take_snapshot_before = ''   ! Physics routine to take a snopshot "before"
  character(32) :: cam_take_snapshot_after  = ''   ! Physics routine to take a snopshot "after"
  integer       :: cam_snapshot_before_num  = -1   ! output history file number for CAM "before" snapshot
  integer       :: cam_snapshot_after_num   = -1   ! output history file number for CAM "after" snapshot

contains

  subroutine phys_ctl_readnl(nlfile)

    use namelist_utils,  only: find_group_name
    use units,           only: getunit, freeunit
    use spmd_utils,      only: mpi_character, mpi_integer, mpi_logical, masterprocid, mpicom
    use cam_control_mod, only: cam_ctrl_set_physics_type

    character(*), intent(in) :: nlfile  ! filepath for file containing namelist input

    integer unitn, ierr
    character(*), parameter :: subname = 'phys_ctl_readnl'

    namelist /phys_ctl_nl/ cam_physpkg, use_simple_phys, cam_chempkg, waccmx_opt,  &
      deep_scheme, shallow_scheme, &
      eddy_scheme, microp_scheme,  macrop_scheme, radiation_scheme, srf_flux_avg, &
      use_subcol_microp, atm_dep_flux, history_amwg, history_vdiag, history_aerosol, history_aero_optics, &
      history_eddy, history_budget,  history_budget_histfile_num, history_waccm, &
      history_waccmx, history_chemistry, history_carma, history_clubb, history_dust, &
      history_cesm_forcing, history_scwaccm_forcing, history_chemspecies_srf, &
      do_clubb_sgs, state_debug_checks, use_hetfrz_classnuc, use_gw_oro, use_gw_front, &
      use_gw_front_igw, use_gw_convect_dp, use_gw_convect_sh, cld_macmic_num_steps, &
      offline_driver, convproc_do_aer, cam_snapshot_before_num, cam_snapshot_after_num, &
      cam_take_snapshot_before, cam_take_snapshot_after

    if (masterproc) then
      unitn = getunit()
      open(unitn, file=trim(nlfile), status='old')
      call find_group_name(unitn, 'phys_ctl_nl', status=ierr)
      if (ierr == 0) then
        read(unitn, phys_ctl_nl, iostat=ierr)
        if (ierr /= 0) then
          call endrun(subname // ':: ERROR reading namelist')
        end if
      end if
      close(unitn)
      call freeunit(unitn)
    end if

    ! Broadcast namelist variables
    call mpi_bcast(deep_scheme,                 len(deep_scheme),      mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(cam_physpkg,                 len(cam_physpkg),      mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(use_simple_phys,             1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(cam_chempkg,                 len(cam_chempkg),      mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(waccmx_opt,                  len(waccmx_opt),       mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(shallow_scheme,              len(shallow_scheme),   mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(eddy_scheme,                 len(eddy_scheme),      mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(microp_scheme,               len(microp_scheme),    mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(radiation_scheme,            len(radiation_scheme), mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(macrop_scheme,               len(macrop_scheme),    mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(srf_flux_avg,                1,                     mpi_integer,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_subcol_microp,           1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(atm_dep_flux,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_amwg,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_vdiag,               1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_eddy,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_aerosol,             1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_aero_optics,         1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_budget,              1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_budget_histfile_num, 1,                     mpi_integer,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_waccm,               1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_waccmx,              1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_chemistry,           1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_carma,               1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_clubb,               1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_cesm_forcing,        1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_chemspecies_srf,     1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_dust,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(history_scwaccm_forcing,     1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(do_clubb_sgs,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(state_debug_checks,          1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_hetfrz_classnuc,         1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_gw_oro,                  1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_gw_front,                1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_gw_front_igw,            1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_gw_convect_dp,           1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(use_gw_convect_sh,           1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(cld_macmic_num_steps,        1,                     mpi_integer,   masterprocid, mpicom, ierr)
    call mpi_bcast(offline_driver,              1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(convproc_do_aer,             1,                     mpi_logical,   masterprocid, mpicom, ierr)
    call mpi_bcast(cam_snapshot_before_num,     1,                     mpi_integer,   masterprocid, mpicom, ierr)
    call mpi_bcast(cam_snapshot_after_num,      1,                     mpi_integer,   masterprocid, mpicom, ierr)
    call mpi_bcast(cam_take_snapshot_before,    len(cam_take_snapshot_before), mpi_character, masterprocid, mpicom, ierr)
    call mpi_bcast(cam_take_snapshot_after,     len(cam_take_snapshot_after),  mpi_character, masterprocid, mpicom, ierr)

    use_spcam = cam_physpkg_is('spcam_sam1mom') .or. cam_physpkg_is('spcam_m2005')

    call cam_ctrl_set_physics_type(cam_physpkg)

    ! Error checking:

    ! Check compatibility of eddy & shallow schemes
    if (shallow_scheme == 'UW' .and. eddy_scheme /= 'diag_TKE') then
      write(iulog,*)'Do you really want to run UW shallow scheme without diagnostic TKE eddy scheme? Quiting'
      call endrun('shallow convection and eddy scheme may be incompatible')
    end if

    if (shallow_scheme == 'Hack' .and. (eddy_scheme /= 'HB' .and. eddy_scheme /= 'HBR')) then
      write(iulog,*)'Do you really want to run Hack shallow scheme with a non-standard eddy scheme? Quiting.'
      call endrun('shallow convection and eddy scheme may be incompatible')
    end if

    ! Check compatibility of PBL and Microphysics schemes
    if (eddy_scheme == 'diag_TKE' .and. microp_scheme == 'RK') then
      write(iulog,*)'UW PBL is not compatible with RK microphysics.  Quiting'
      call endrun('PBL and Microphysics schemes incompatible')
    end if

    ! Add a check to make sure CLUBB and MG are used together
    if (do_clubb_sgs .and. (microp_scheme /= 'MG') .and. .not. use_spcam) then
      write(iulog,*)'CLUBB is only compatible with MG microphysics.  Quiting'
      call endrun('CLUBB and microphysics schemes incompatible')
    end if

    ! Check that eddy_scheme, macrop_scheme, shallow_scheme are all set to CLUBB_SGS if do_clubb_sgs is true
    if (do_clubb_sgs .and. .not. use_spcam) then
      if (eddy_scheme /= 'CLUBB_SGS' .or. macrop_scheme /= 'CLUBB_SGS' .or. shallow_scheme /= 'CLUBB_SGS') then
        write(iulog, *) 'eddy_scheme, macrop_scheme and shallow_scheme must all be CLUBB_SGS.  Quiting'
        call endrun('CLUBB and eddy, macrop or shallow schemes incompatible')
      end if
    end if

    ! Macro/micro co-substepping support.
    if (cld_macmic_num_steps > 1) then
      if (microp_scheme /= 'MG' .or. (macrop_scheme /= 'park' .and. macrop_scheme /= 'CLUBB_SGS')) then
        call endrun ('Setting cld_macmic_num_steps > 1 is only ' // &
            'supported with Park or CLUBB macrophysics and MG microphysics.')
      end if
    end if

    ! prog_modal_aero determines whether prognostic modal aerosols are present in the run.
    prog_modal_aero = index(cam_chempkg,'_mam')>0

  end subroutine phys_ctl_readnl

  pure logical function cam_physpkg_is(name) result(res)

    character(*), intent(in) :: name

    res = trim(name) == trim(cam_physpkg)

  end function cam_physpkg_is

  pure logical function cam_chempkg_is(name) result(res)

    character(*), intent(in) :: name

    res = trim(name) == trim(cam_chempkg)

  end function cam_chempkg_is

  pure logical function waccmx_is(name) result(res)

    character(*), intent(in) :: name

    res = trim(name) == trim(waccmx_opt)

  end function waccmx_is

  subroutine phys_getopts(deep_scheme_out, shallow_scheme_out, eddy_scheme_out, microp_scheme_out, &
                          radiation_scheme_out, use_subcol_microp_out, atm_dep_flux_out, &
                          history_amwg_out, history_vdiag_out, history_aerosol_out, history_aero_optics_out, history_eddy_out, &
                          history_budget_out, history_budget_histfile_num_out, &
                          history_waccm_out, history_waccmx_out, history_chemistry_out, &
                          history_carma_out, history_clubb_out, history_dust_out, &
                          history_cesm_forcing_out, history_scwaccm_forcing_out, history_chemspecies_srf_out, &
                          cam_chempkg_out, prog_modal_aero_out, macrop_scheme_out, &
                          do_clubb_sgs_out, use_spcam_out, state_debug_checks_out, cld_macmic_num_steps_out, &
                          offline_driver_out, convproc_do_aer_out, cam_snapshot_before_num_out, cam_snapshot_after_num_out,&
                          cam_take_snapshot_before_out, cam_take_snapshot_after_out)

    character(*), intent(out), optional :: deep_scheme_out
    character(*), intent(out), optional :: shallow_scheme_out
    character(*), intent(out), optional :: eddy_scheme_out
    character(*), intent(out), optional :: microp_scheme_out
    character(*), intent(out), optional :: radiation_scheme_out
    character(*), intent(out), optional :: macrop_scheme_out
    logical     , intent(out), optional :: use_subcol_microp_out
    logical     , intent(out), optional :: use_spcam_out
    logical     , intent(out), optional :: atm_dep_flux_out
    logical     , intent(out), optional :: history_amwg_out
    logical     , intent(out), optional :: history_vdiag_out
    logical     , intent(out), optional :: history_eddy_out
    logical     , intent(out), optional :: history_aerosol_out
    logical     , intent(out), optional :: history_aero_optics_out
    logical     , intent(out), optional :: history_budget_out
    integer     , intent(out), optional :: history_budget_histfile_num_out
    logical     , intent(out), optional :: history_waccm_out
    logical     , intent(out), optional :: history_waccmx_out
    logical     , intent(out), optional :: history_chemistry_out
    logical     , intent(out), optional :: history_carma_out
    logical     , intent(out), optional :: history_clubb_out
    logical     , intent(out), optional :: history_cesm_forcing_out
    logical     , intent(out), optional :: history_chemspecies_srf_out
    logical     , intent(out), optional :: history_dust_out
    logical     , intent(out), optional :: history_scwaccm_forcing_out
    logical     , intent(out), optional :: do_clubb_sgs_out
    character(*), intent(out), optional :: cam_chempkg_out
    logical     , intent(out), optional :: prog_modal_aero_out
    logical     , intent(out), optional :: state_debug_checks_out
    integer     , intent(out), optional :: cld_macmic_num_steps_out
    logical     , intent(out), optional :: offline_driver_out
    logical     , intent(out), optional :: convproc_do_aer_out
    integer     , intent(out), optional :: cam_snapshot_before_num_out
    integer     , intent(out), optional :: cam_snapshot_after_num_out
    character(*), intent(out), optional :: cam_take_snapshot_before_out
    character(*), intent(out), optional :: cam_take_snapshot_after_out

    if (present(deep_scheme_out                )) deep_scheme_out                 = deep_scheme
    if (present(shallow_scheme_out             )) shallow_scheme_out              = shallow_scheme
    if (present(eddy_scheme_out                )) eddy_scheme_out                 = eddy_scheme
    if (present(microp_scheme_out              )) microp_scheme_out               = microp_scheme
    if (present(radiation_scheme_out           )) radiation_scheme_out            = radiation_scheme
    if (present(use_subcol_microp_out          )) use_subcol_microp_out           = use_subcol_microp
    if (present(use_spcam_out                  )) use_spcam_out                   = use_spcam
    if (present(macrop_scheme_out              )) macrop_scheme_out               = macrop_scheme
    if (present(atm_dep_flux_out               )) atm_dep_flux_out                = atm_dep_flux
    if (present(history_aerosol_out            )) history_aerosol_out             = history_aerosol
    if (present(history_aero_optics_out        )) history_aero_optics_out         = history_aero_optics
    if (present(history_budget_out             )) history_budget_out              = history_budget
    if (present(history_amwg_out               )) history_amwg_out                = history_amwg
    if (present(history_vdiag_out              )) history_vdiag_out               = history_vdiag
    if (present(history_eddy_out               )) history_eddy_out                = history_eddy
    if (present(history_budget_histfile_num_out)) history_budget_histfile_num_out = history_budget_histfile_num
    if (present(history_waccm_out              )) history_waccm_out               = history_waccm
    if (present(history_waccmx_out             )) history_waccmx_out              = history_waccmx
    if (present(history_chemistry_out          )) history_chemistry_out           = history_chemistry
    if (present(history_cesm_forcing_out       )) history_cesm_forcing_out        = history_cesm_forcing
    if (present(history_chemspecies_srf_out    )) history_chemspecies_srf_out     = history_chemspecies_srf
    if (present(history_scwaccm_forcing_out    )) history_scwaccm_forcing_out     = history_scwaccm_forcing
    if (present(history_carma_out              )) history_carma_out               = history_carma
    if (present(history_clubb_out              )) history_clubb_out               = history_clubb
    if (present(history_dust_out               )) history_dust_out                = history_dust
    if (present(do_clubb_sgs_out               )) do_clubb_sgs_out                = do_clubb_sgs
    if (present(cam_chempkg_out                )) cam_chempkg_out                 = cam_chempkg
    if (present(prog_modal_aero_out            )) prog_modal_aero_out             = prog_modal_aero
    if (present(state_debug_checks_out         )) state_debug_checks_out          = state_debug_checks
    if (present(cld_macmic_num_steps_out       )) cld_macmic_num_steps_out        = cld_macmic_num_steps
    if (present(offline_driver_out             )) offline_driver_out              = offline_driver
    if (present(convproc_do_aer_out            )) convproc_do_aer_out             = convproc_do_aer
    if (present(cam_snapshot_before_num_out    )) cam_snapshot_before_num_out     = cam_snapshot_before_num
    if (present(cam_snapshot_after_num_out     )) cam_snapshot_after_num_out      = cam_snapshot_after_num
    if (present(cam_take_snapshot_before_out   )) cam_take_snapshot_before_out    = cam_take_snapshot_before
    if (present(cam_take_snapshot_after_out    )) cam_take_snapshot_after_out     = cam_take_snapshot_after

  end subroutine phys_getopts

  subroutine phys_setopts(fv_am_correction_in)

    logical, intent(in), optional :: fv_am_correction_in

    if (present(fv_am_correction_in)) fv_am_correction = fv_am_correction_in

  end subroutine phys_setopts

  pure logical function phys_deepconv_pbl() result(res)

    ! Don't allow deep convection in PBL if running UW PBL scheme
    res = eddy_scheme == 'diag_TKE' .or. shallow_scheme == 'UW'

  end function phys_deepconv_pbl

  pure logical function phys_do_flux_avg() result(res)

    res = srf_flux_avg == 1

  end function phys_do_flux_avg

end module phys_control
